#include <Uefi.h>
#include <Base.h>
#include <IndustryStandard/SmBios.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/IoLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/FileHandleLib.h>
#include "SmbiosApp.h"


UINT8  *gSmbiosPointer = NULL;
UINT8  *gSmbiosTablePointer = NULL;

UINT8 *
GetSmbiosEntryPointer ()
{
  UINT16  Index = 0;
  UINT32  Data = 0;
  UINT8   *SmbiosPointer  = NULL;
  UINT8   *Smbios3Pointer = NULL;

  Data = MmioRead32 (SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index);
  while ((Data != SMBIOS_SIGNATURE) && ((SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index) != SMBIOS_ENTRY_BASE_REGIN_END)) {
    Index++;
    Data = MmioRead32 (SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index);
  }

  if ((SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index) < SMBIOS_ENTRY_BASE_REGIN_END) {
    Print (L"Search Smbios entry pointer success, Address: 0x%lx\n", SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index);
    SmbiosPointer = (UINT8 *)(UINTN)(SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index);
  }

  Index = 0;
  Data = MmioRead32 (SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index);
  while ((Data != SMBIOS3_SIGNATURE) && ((SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index) != SMBIOS_ENTRY_BASE_REGIN_END)) {
    Index++;
    Data = MmioRead32 (SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index);
  }

  if ((SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index) < SMBIOS_ENTRY_BASE_REGIN_END) {
    if (MmioRead8 (SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index + 4) == 0x5F) {
      Print (L"Search Smbios3 entry pointer success, Address: 0x%lx\n", SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index);
      Smbios3Pointer = (UINT8 *)(UINTN)(SMBIOS_ENTRY_BASE_REGIN_BEGIN + Index);
    }
  }

  if ((SmbiosPointer == NULL) && (Smbios3Pointer == NULL)) {
    Print (L"Search Smbios or Smbios3 entry pointer error\n");
    return NULL;
  }

  if (Smbios3Pointer == NULL) {
    return SmbiosPointer;
  }

  return Smbios3Pointer;
}

UINT8 *
GetSmbiosTablePointer (
  OUT UINT16  *TableLength,
  OUT UINT16  *NumOfSmbiosStructures
  )
{
  UINT32  Signature = 0;
  SMBIOS_TABLE_ENTRY_POINT      *SmbiosEntryPointer  = NULL;
  SMBIOS_TABLE_3_0_ENTRY_POINT  *Smbios3EntryPointer = NULL;

  Signature = *(UINT32 *)(gSmbiosPointer);
  if (Signature == SMBIOS_SIGNATURE) {
    SmbiosEntryPointer = (SMBIOS_TABLE_ENTRY_POINT *)(gSmbiosPointer);
    *TableLength = SmbiosEntryPointer->TableLength;
    *NumOfSmbiosStructures = SmbiosEntryPointer->NumberOfSmbiosStructures;
    Print (L"Smbios MajorV: %d, MinorV: %d, TableLength: 0x%04x, Num: %d\n", 
              SmbiosEntryPointer->MajorVersion,
              SmbiosEntryPointer->MinorVersion,
              SmbiosEntryPointer->TableLength,
              SmbiosEntryPointer->NumberOfSmbiosStructures
              );
    return (UINT8 *)(UINT64)(SmbiosEntryPointer->TableAddress);
  } else if (Signature == SMBIOS3_SIGNATURE) {
    Smbios3EntryPointer = (SMBIOS_TABLE_3_0_ENTRY_POINT *)(gSmbiosPointer);
    *TableLength = 0;
    *NumOfSmbiosStructures = 0;
    Print (L"Smbios3 MajorV: %d, MinorV: %d\n", 
              Smbios3EntryPointer->MajorVersion,
              Smbios3EntryPointer->MinorVersion
              );
    return (UINT8 *)(UINT64)(Smbios3EntryPointer->TableAddress);
  } else {
    Print (L"Signature error\n");
  }

  return NULL;
}

VOID
ListSmbiosTable (
  IN UINT16  TableLength,
  IN UINT16  Type
  )
{
  UINT16 NextTypeOffset = 0;
  SMBIOS_STRUCTURE  *Header = NULL;
  UINT16  HaveStringTextFlag = 0;
  UINT16  NextStringOffset  = 0;
  UINT16  NextStringLength  = 0;
  UINT16  StringIndex       = 1;
  UINT16  StringTextEndFlag = 0;

  if (TableLength <= sizeof (SMBIOS_STRUCTURE)) {
    Print (L"Unknow error\n");
    return;
  }

  while ((NextTypeOffset < TableLength) && ((TableLength - NextTypeOffset) > sizeof (SMBIOS_STRUCTURE))) {
    Header = (SMBIOS_STRUCTURE *)(gSmbiosTablePointer + NextTypeOffset);
    if ((Type == Header->Type) || (Type == 0xFFFF)) {
      Print (L"Smbios Type: 0x%02x\n", Header->Type);
    }

    HaveStringTextFlag = *(UINT16 *)(gSmbiosTablePointer + NextTypeOffset + Header->Length);
    if (HaveStringTextFlag == 0) {
      if ((Type == Header->Type) || (Type == 0xFFFF)) {
        Print (L"  No have string text\n");
      }
      NextTypeOffset = NextTypeOffset + Header->Length + 2;
      continue;
    }

    NextStringOffset = NextTypeOffset + Header->Length;
    StringTextEndFlag = *(UINT16 *)(gSmbiosTablePointer + NextStringOffset);
    while (StringTextEndFlag != 0) {
      NextStringLength = (UINT16)AsciiStrLen ((gSmbiosTablePointer + NextStringOffset));
      if ((Type == Header->Type) || (Type == 0xFFFF)) {
        Print (L"  String %d:  %a\n", StringIndex, (UINT8 *)(gSmbiosTablePointer + NextStringOffset));
      }

      NextStringOffset = NextStringOffset + NextStringLength;
      StringTextEndFlag = *(UINT16 *)(gSmbiosTablePointer + NextStringOffset);
      if (StringTextEndFlag != 0) {
        NextStringOffset = NextStringOffset + 1;
      } else {
        NextStringOffset = NextStringOffset + 2;
      }
      StringIndex++;
    }
    StringIndex = 1;
    NextTypeOffset = NextStringOffset;
  }
}


EFI_STATUS
EFIAPI
CreatFileDataByName (
  IN  CHAR16     *FileName,
  IN  UINT8      *Buffer,
  IN  UINT16     Size
  )
{
  EFI_STATUS                      Status = EFI_SUCCESS;
  EFI_FILE_PROTOCOL               *FileRoot;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
  EFI_FILE_PROTOCOL               *FileHandle = NULL;
  UINTN                           FileSize   = 0;
  EFI_HANDLE                      *HandleBuffer = NULL;
  UINTN                           HandleCount = 0;
  UINTN                           Index;

  if (FileName == NULL) {
    Print (L"FileName Error\n");
    return EFI_ABORTED;
  }

  Status = gBS->LocateHandleBuffer(
            ByProtocol,
            &gEfiSimpleFileSystemProtocolGuid,
            NULL,
            &HandleCount,
            &HandleBuffer
          );
  if (EFI_ERROR (Status) || HandleCount == 0) {
    Print (L"Locate gEfiSimpleFileSystemProtocolGuid HandleBuffer Fail\n");
    return EFI_NOT_READY;
  }

  for (Index = 0; Index < HandleCount; Index++) {
    Status = gBS->HandleProtocol(
              HandleBuffer[Index],
              &gEfiSimpleFileSystemProtocolGuid,
              (VOID **)&SimpleFileSystem
              );
    if (EFI_ERROR(Status)) {
      continue;
    }

    Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &FileRoot);
    if (EFI_ERROR(Status)) {
      continue;
    }

    Status = FileRoot->Open(FileRoot, &FileHandle, FileName, (EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ), EFI_FILE_ARCHIVE);
    if (EFI_ERROR(Status)) {
      continue;
    } else {
      break;
    }
  }

  if (EFI_ERROR(Status)) {
    Print (L"Open File Fail\n");
    return EFI_ABORTED;
  }

  FileSize = Size;
  Status = FileHandle->Write (FileHandle, &FileSize, Buffer);
  if(EFI_ERROR(Status)) {
    Print(L"Wirte File data fail\n");
  }

  FileRoot->Close(FileHandle);

  return Status;
}

